package lt.egidijus.aatest.popup.actions;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import lt.egidijus.aatest.BrowserView;
import lt.egidijus.aatest.analyzer.Analyzer;
import lt.egidijus.aatest.analyzer.AnalyzerImpl;
import lt.egidijus.aatest.filters.ATGOption;
import lt.egidijus.aatest.filters.AbstractClassFilter;
import lt.egidijus.aatest.filters.ConstructorFilter;
import lt.egidijus.aatest.filters.EnumFilter;
import lt.egidijus.aatest.filters.InterfaceFilter;
import lt.egidijus.aatest.generator.Generator;
import lt.egidijus.aatest.generator.GeneratorImpl;
import lt.egidijus.aatest.generator.GeneticGenerator;
import lt.egidijus.aatest.generator.rules.Rule;
import lt.egidijus.aatest.genetic.CoverageParser;
import lt.egidijus.aatest.genetic.GeneticClass;
import lt.egidijus.aatest.genetic.GeneticMethod;
import lt.egidijus.aatest.utils.Constants;
import lt.egidijus.aatest.utils.LoggerComponent;
import lt.egidijus.aatest.utils.Utils;
import lt.egidijus.aatest.wizards.helpers.DefaultOptions;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;

public class GeneticGeneratorAction extends lt.egidijus.aatest.utils.LoggerComponent {
	final Shell shell;
	String generationTime = "";
	public String getPerformanceTimes() {
		return 	generationTime;
	}

	List<String> failedClasses = new ArrayList<String>(0);
	ArrayList<GeneticClass> geneticClass = new ArrayList<GeneticClass>();

	public GeneticGeneratorAction(final List<ICompilationUnit> units,
			final Shell shell) {
		ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);
		this.shell = shell;
		try {
			dialog.run(true, true, new IRunnableWithProgress() {
				@Override
				public void run(IProgressMonitor monitor) {
					long t1 = System.nanoTime();
					try {
						System.gc();
						generate(units, monitor);
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						long t2 = System.nanoTime();
						System.gc();
						generationTime = String.format("\nExecution time -%8.2f s.", (t2 - t1)/1000000000.0);
					}

				}
			});
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	StringBuilder log = new StringBuilder();

	public boolean isAnyError() {
		return failedClasses.size() > 0;
	}

	public String getFailedClasses() {
		StringBuilder b = new StringBuilder("Failed classes:\n");
		for (String s : failedClasses) {
			b.append("\t" + s + "\n");
		}
		return b.toString();
	}

	public String getMessage() {
		return log.toString();
	}

	private void generate(List<ICompilationUnit> units, IProgressMonitor monitor) {
		errorsList.clear();
		Rule defaultRule = (Rule) DefaultOptions.getDefaultRule();
		List<Rule> rules = new ArrayList<Rule>();
		for (ATGOption t : DefaultOptions.getDefaultRules()) {
			rules.add((Rule) t);
		}
		List<ConstructorFilter> defaultConstructors = new ArrayList<ConstructorFilter>();
		for (ATGOption t : DefaultOptions.getDefaultConstructors()) {
			defaultConstructors.add((ConstructorFilter) t);
		}
		List<InterfaceFilter> defaultInterfaces = new ArrayList<InterfaceFilter>();
		for (ATGOption t : DefaultOptions.getDefaultInterfaces()) {
			defaultInterfaces.add((InterfaceFilter) t);
		}
		List<AbstractClassFilter> abstractClasses = new ArrayList<AbstractClassFilter>();
		for (ATGOption t : DefaultOptions.getDefaultAclasses()) {
			abstractClasses.add((AbstractClassFilter) t);
		}
		List<EnumFilter> enums = new ArrayList<EnumFilter>();
		for (ATGOption t : DefaultOptions.getDefaultEnums()) {
			enums.add((EnumFilter) t);
		}

		ConstructorFilter c = (ConstructorFilter) DefaultOptions
				.getDefaultConstructor();

		AbstractClassFilter a = (AbstractClassFilter) DefaultOptions
				.getDefaultAbtractClassFilter();

		InterfaceFilter di = (InterfaceFilter) DefaultOptions
				.getDefaultInterface();

		EnumFilter eF = (EnumFilter) DefaultOptions.getDefaultEnumFilter();

        MessageConsole myConsole = findConsole("Coverage"); 
        MessageConsoleStream out = myConsole.newMessageStream();
        myConsole.activate();
		monitor.beginTask("Generating coverage report..", units.size()*Constants.geneticPopulation);
		int i = 0;

		for (ICompilationUnit t : units) {
			i++;
			LoggerComponent fullLog = new LoggerComponent();
			fullLog.setName(t.getElementName());
			fullLog.setLog(TYPES.LOG,
					"Generating test for " + t.getElementName());

			try {
				if (!Flags.isAbstract(t.getAllTypes()[0].getFlags())
						&& !Flags.isInterface(t.getAllTypes()[0].getFlags())) {
					Analyzer aa = new AnalyzerImpl(defaultConstructors,
							abstractClasses, defaultInterfaces, enums, c, a,
							di, eF);
					boolean poz = aa.analyze(t);
					if (!aa.isErrors() && poz) {						
						GeneticGenerator g = new GeneticGenerator(rules, defaultRule);
						for (int k = 0; k < Constants.geneticPopulation; k++){
							g.generate(t, aa.getGeneratedResult());
							GeneticClass gClass = g.getGeneticClass();
							calculateCoverage(gClass.getPackageName()+"."+gClass.getClassName()+Constants.testClassPostfix,monitor,out);
							getCoverageFromXML(gClass);
							monitor.worked(i);
							geneticClass.add(gClass);
							i++;					
						}
						StringBuilder classBuilder = getGeneticClass();
						if (classBuilder != null)
							g.saveToFile(t, classBuilder);
						geneticClass.clear();
						if (!g.isErrors()) {
							fullLog.getErrorsList().addAll(g.getErrorsList());
						}
					} else {
						fullLog.getErrorsList().addAll(aa.getErrorsList());
					}

				} else {

					fullLog.setLog(
							TYPES.GENERIC,
							"Test will not be generated for "
									+ t.getElementName()
									+ " because it is interface or abstract class");

				}
			} catch (JavaModelException e) {
				fullLog.setLog(TYPES.GENERIC, "Test will not be generated for "
						+ t.getElementName() + " ");

			}
			log.append(fullLog.getErrors());
			if (!fullLog.isSuccess()) {
				failedClasses.add(t.getElementName());
			}
		}

		monitor.done();
	}
	
	private StringBuilder getGeneticClass(){
		int bestCoverage = -1;
		int changeTime = 0;
		GeneticClass  bestClass = null;
		for (GeneticClass gClass: geneticClass){
			if (gClass.getClassCoverageIntValue() != null && gClass.getClassCoverageIntValue() > bestCoverage){
				bestClass = gClass;
				bestCoverage = gClass.getClassCoverageIntValue().intValue();
				changeTime++;
			}
		}
		if (changeTime > 1){
			StringBuilder b = new StringBuilder();
			b.append(bestClass.getClassHeader());
			for (GeneticMethod m: bestClass.getMethods()){
				for (String s: m.getMethodBodies())
				b.append(s);
			}
			b.append(bestClass.getClassFooter());
			return b;
			
		}		
		return null;
	}
	
	private void getCoverageFromXML(GeneticClass gClass){
		CoverageParser parser = new CoverageParser(gClass);
		final String path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toString();
	     parser.parsingXml(new File(path +"/"+ Constants.testProjectName +"/coverage/coverage.xml"));
	}
	
	private void calculateCoverage(String qualityClassName,  IProgressMonitor monitor, MessageConsoleStream out){
		try {

			final String path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toString();
			String updateMainProject = "android update project -p \"" + path +"/"+ Constants.projectName +"\"";
			String buildMainProject = "ant -f \"" + path +"/"+ Constants.projectName +"/build.xml\" clean debug";
	
			String updateProject = "android update test-project -m \"" + path +"/"+ Constants.projectName +"\" -p \"" + path +"/"+ Constants.testProjectName +"\"";
			String buildCoverageReport = "ant -f \"" + path +"/"+ Constants.testProjectName +"/build.xml\" all clean emma debug install test-one";
			String buildXMLReport = "java emma report -r xml -in \"" + path +"/"+ Constants.testProjectName +"/coverage/coverage.em\" -in \"" 
			+ path +"/"+ Constants.testProjectName +"/coverage/coverage.ec\"" + " -Dreport.xml.out.file=\""+path +"/"+ Constants.testProjectName +"/coverage/coverage.xml\"";
	

			//String komanda = "ping google.com";
	        System.out.println(updateMainProject);
	        System.out.println(buildMainProject);
	       System.out.println(buildXMLReport);
	        System.out.println(updateProject);
	        System.out.println(buildCoverageReport);
	       System.out.println(buildXMLReport);
		//	monitor.subTask("Update main project..");
	    //    boolean exitVal = commandLine(updateMainProject, out, monitor);
		//	monitor.worked(2);
	//				        if (exitVal){
	//							monitor.subTask("Build main project..");
	//				        	exitVal = commandLine(buildMainProject, out, monitor);
	//							monitor.worked(3);
	//				        }
	      //  if (exitVal){
	        boolean	exitVal = commandLine(updateProject, out, monitor);
	
	        //}
			Utils.setTestToCoverage("class", qualityClassName);
	        if (exitVal){
	        	exitVal = commandLine(buildCoverageReport, out, monitor);
	        }
	        if (exitVal){
	        	exitVal = commandLine(buildXMLReport, out, monitor);
	        }
		//	boolean exitVal = true;

		 

		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private boolean commandLine(String command, MessageConsoleStream output, IProgressMonitor monitor ) throws InterruptedException, IOException{
		String[] cmd = new String[3];
        cmd[0] = "cmd.exe" ;
        cmd[1] = "/C" ;
        cmd[2] = command;
        Runtime rt = Runtime.getRuntime();
        Process proc = rt.exec(cmd);
        
        InputStreamReader is = new InputStreamReader(proc.getInputStream());
        BufferedReader br = new BufferedReader(is);   
        String line=null;
    	 int i =0;
		 while ( (line = br.readLine()) != null){
				i++;
		//		monitor.subTask("I'm doing something here " + i);
			    output.println("INFO> " + line); 
			//	monitor.worked(1);
			}
		  InputStreamReader isr = new InputStreamReader(proc.getErrorStream());
	      BufferedReader brr = new BufferedReader(isr);   
	      line=null;
	      int error =0;
	      while ( (line = brr.readLine()) != null){
	    	  	error++;
			    output.println("ERROR> " + line); 
				//monitor.worked(1);
		  }

         int exitVal = proc.waitFor();
         if (exitVal == 0 && error == 0)
        	 return true;
         return false;
	}
	
	
    private static MessageConsole findConsole(String name) {
	      ConsolePlugin plugin = ConsolePlugin.getDefault();
	      IConsoleManager conMan = plugin.getConsoleManager();
	      IConsole[] existing = conMan.getConsoles();
	      for (int i = 0; i < existing.length; i++)
	         if (name.equals(existing[i].getName()))
	            return (MessageConsole) existing[i];
	      //no console found, so create a new one
	      MessageConsole myConsole = new MessageConsole(name, null);
	      conMan.addConsoles(new IConsole[]{myConsole});
	      return myConsole;
  }

}
